﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CSharp.Libs.PathSyncer
{
    #region event handlers
    public delegate void PathScannerProgressEventHandler(object sender, PathScannerProgressEventArgs e);
    public class PathScannerProgressEventArgs
    {
        public PathScannerProgressType ProgressType { get; set; }
        public int FileCount { get; set; }
        public int CurrentFileIndex { get; set; }
        public string CurrentFileName { get; set; }
    }
    #endregion



    public class PathScanner : IDisposable
    {
        #region declarations
        private bool _fAsyncCall = false;

        private Thread _thrCalculateHash;

        #region event handling
        public event PathScannerProgressEventHandler PathScannerProgress;
        protected void OnPathScannerProgress(
            PathScannerProgressEventArgs ea)
        {
            if (PathScannerProgress != null)
            {
                PathScannerProgress(this, ea);
            }
        }
        #endregion
        #endregion



        #region constructor
        public PathScanner()
        {

        }

        #region dispose pattern (http://www.codeproject.com/Articles/15360/Implementing-IDisposable-and-the-Dispose-Pattern-P)
        // some fields that require cleanup
        private bool disposed = false; // to detect redundant calls

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    // clean up managed handles
                    PathScannerProgress = null;
                }

                disposed = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }
        #endregion
        #endregion



        #region properties
        public SyncPath SyncPathObject { get; set; }
        #endregion



        #region methods
        public void ScanPath(object stateInfo)
        {
            try
            {
                if (SyncPathObject == null)
                {
                    throw new ArgumentNullException("SyncPathObject", "Argument is null");
                }

                if (!Directory.Exists(SyncPathObject.Path))
                {
                    throw new ArgumentException("SyncPathObject", "Directory not found");
                }

                //prepare event args
                PathScannerProgressEventArgs ea = new PathScannerProgressEventArgs()
                {
                    ProgressType = PathScannerProgressType.Started,
                    FileCount = 0,
                    CurrentFileIndex = 0,
                    CurrentFileName = "",
                };

                //Throw progress
                OnPathScannerProgress(ea);

                //get list of files
                string[] strFiles = Directory.GetFiles(SyncPathObject.Path, "*.*", SearchOption.AllDirectories);

                //Throw progress
                ea.ProgressType = PathScannerProgressType.GotFileCount;
                ea.FileCount = strFiles.Count();
                OnPathScannerProgress(ea);

                //prepare index
                int idx = 0;

                //cycle through
                foreach (string strFile in strFiles)
                {
                    //Throw progress
                    ea.ProgressType = PathScannerProgressType.WorkingOnFile;
                    ea.CurrentFileIndex = idx;
                    ea.CurrentFileName = strFile;
                    OnPathScannerProgress(ea);

                    //create FileObject
                    FileObject fo = new FileObject()
                    {
                        FileName = Path.GetFileName(strFile),
                        Path = Path.GetDirectoryName(strFile),
                        SyncPathId = SyncPathObject.SyncPathId,
                    };

                    if (_fAsyncCall)
                    {
                        using (FileHashCalculator fhc = new FileHashCalculator())
                        {
                            fhc.HashFileObject = fo;
                            fhc.CalculateFileHashAsync();
                        }
                    }
                    else
                    {
                        using (FileHashCalculator fhc = new FileHashCalculator())
                        {
                            fhc.HashFileObject = fo;
                            fhc.CalculateFileHash();
                        }
                    }

                    //increment index
                    idx++;
                }

                //Throw progress
                ea.ProgressType = PathScannerProgressType.Finished;
                ea.CurrentFileIndex = 0;
                ea.CurrentFileName = "";
                OnPathScannerProgress(ea);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (_fAsyncCall) _fAsyncCall = false;
            }
        }

        public void ScanPathAsync()
        {
            _fAsyncCall = true;

            //_thrCalculateHash = new Thread(ScanPath);
            //_thrCalculateHash.Start();

            ThreadPool.QueueUserWorkItem(ScanPath);
        }
        #endregion
    }
}
